home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume9 / uemacs3.8b / part09 < prev    next >
Encoding:
Internet Message Format  |  1987-03-16  |  38.8 KB

  1. Subject:  v09i041:  MicroEMACS, version 3.8b, Part09/14
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.TMC.COM
  4.  
  5. Submitted by: ihnp4!itivax!duncan!lawrence (Daniel Lawrence)
  6. Mod.sources: Volume 9, Issue 41
  7. Archive-name: uemacs3.8b/Part09
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line,
  11. # then unpack it by saving it in a file and typing "sh file".
  12. # If this archive is complete, you will see the message:
  13. #        "End of archive 9 (of 14)."
  14. # Contents:  file.c isearch.c
  15. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  16. echo shar: Extracting \"file.c\" \(18229 characters\)
  17. if test -f file.c ; then 
  18.   echo shar: Will not over-write existing file \"file.c\"
  19. else
  20. sed "s/^X//" >file.c <<'END_OF_file.c'
  21. X/*    FILE.C:   for MicroEMACS
  22. X
  23. X    The routines in this file handle the reading, writing
  24. X    and lookup of disk files.  All of details about the
  25. X    reading and writing of the disk are in "fileio.c".
  26. X
  27. X*/
  28. X
  29. X#include        <stdio.h>
  30. X#include    "estruct.h"
  31. X#include        "edef.h"
  32. X
  33. X#if    MEGAMAX
  34. Xoverlay "file"
  35. X#endif
  36. X
  37. X/*
  38. X * Read a file into the current
  39. X * buffer. This is really easy; all you do it
  40. X * find the name of the file, and call the standard
  41. X * "read a file into the current buffer" code.
  42. X * Bound to "C-X C-R".
  43. X */
  44. Xfileread(f, n)
  45. X{
  46. X        register int    s;
  47. X        char fname[NFILEN];
  48. X
  49. X    if (restflag)        /* don't allow this command if restricted */
  50. X        return(resterr());
  51. X        if ((s=mlreply("Read file: ", fname, NFILEN)) != TRUE)
  52. X                return(s);
  53. X        return(readin(fname, TRUE));
  54. X}
  55. X
  56. X/*
  57. X * Insert a file into the current
  58. X * buffer. This is really easy; all you do it
  59. X * find the name of the file, and call the standard
  60. X * "insert a file into the current buffer" code.
  61. X * Bound to "C-X C-I".
  62. X */
  63. Xinsfile(f, n)
  64. X{
  65. X        register int    s;
  66. X        char fname[NFILEN];
  67. X
  68. X    if (restflag)        /* don't allow this command if restricted */
  69. X        return(resterr());
  70. X    if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  71. X        return(rdonly());    /* we are in read only mode    */
  72. X        if ((s=mlreply("Insert file: ", fname, NFILEN)) != TRUE)
  73. X                return(s);
  74. X        return(ifile(fname));
  75. X}
  76. X
  77. X/*
  78. X * Select a file for editing.
  79. X * Look around to see if you can find the
  80. X * fine in another buffer; if you can find it
  81. X * just switch to the buffer. If you cannot find
  82. X * the file, create a new buffer, read in the
  83. X * text, and switch to the new buffer.
  84. X * Bound to C-X C-F.
  85. X */
  86. Xfilefind(f, n)
  87. X{
  88. X        char fname[NFILEN];    /* file user wishes to find */
  89. X        register int s;        /* status return */
  90. X
  91. X    if (restflag)        /* don't allow this command if restricted */
  92. X        return(resterr());
  93. X        if ((s=mlreply("Find file: ", fname, NFILEN)) != TRUE)
  94. X                return(s);
  95. X    return(getfile(fname, TRUE));
  96. X}
  97. X
  98. Xviewfile(f, n)    /* visit a file in VIEW mode */
  99. X{
  100. X        char fname[NFILEN];    /* file user wishes to find */
  101. X        register int s;        /* status return */
  102. X    register WINDOW *wp;    /* scan for windows that need updating */
  103. X
  104. X    if (restflag)        /* don't allow this command if restricted */
  105. X        return(resterr());
  106. X        if ((s=mlreply("View file: ", fname, NFILEN)) != TRUE)
  107. X                return (s);
  108. X    s = getfile(fname, FALSE);
  109. X    if (s) {    /* if we succeed, put it in view mode */
  110. X        curwp->w_bufp->b_mode |= MDVIEW;
  111. X
  112. X        /* scan through and update mode lines of all windows */
  113. X        wp = wheadp;
  114. X        while (wp != NULL) {
  115. X            wp->w_flag |= WFMODE;
  116. X            wp = wp->w_wndp;
  117. X        }
  118. X    }
  119. X    return(s);
  120. X}
  121. X
  122. X#if    CRYPT
  123. Xresetkey()    /* reset the encryption key if needed */
  124. X
  125. X{
  126. X    register int s;    /* return status */
  127. X
  128. X    /* turn off the encryption flag */
  129. X    cryptflag = FALSE;
  130. X
  131. X    /* if we are in crypt mode */
  132. X    if (curbp->b_mode & MDCRYPT) {
  133. X        if (curbp->b_key[0] == 0) {
  134. X            s = setkey(FALSE, 0);
  135. X            if (s != TRUE)
  136. X                return(s);
  137. X        }
  138. X
  139. X        /* let others know... */
  140. X        cryptflag = TRUE;
  141. X
  142. X        /* and set up the key to be used! */
  143. X        /* de-encrypt it */
  144. X        crypt((char *)NULL, 0);
  145. X        crypt(curbp->b_key, strlen(curbp->b_key));
  146. X
  147. X        /* re-encrypt it...seeding it to start */
  148. X        crypt((char *)NULL, 0);
  149. X        crypt(curbp->b_key, strlen(curbp->b_key));
  150. X    }
  151. X
  152. X    return(TRUE);
  153. X}
  154. X#endif
  155. X
  156. Xgetfile(fname, lockfl)
  157. X
  158. Xchar fname[];        /* file name to find */
  159. Xint lockfl;        /* check the file for locks? */
  160. X
  161. X{
  162. X        register BUFFER *bp;
  163. X        register LINE   *lp;
  164. X        register int    i;
  165. X        register int    s;
  166. X        char bname[NBUFN];    /* buffer name to put file */
  167. X
  168. X        for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
  169. X                if ((bp->b_flag&BFINVS)==0 && strcmp(bp->b_fname, fname)==0) {
  170. X            swbuffer(bp);
  171. X                        lp = curwp->w_dotp;
  172. X                        i = curwp->w_ntrows/2;
  173. X                        while (i-- && lback(lp)!=curbp->b_linep)
  174. X                                lp = lback(lp);
  175. X                        curwp->w_linep = lp;
  176. X                        curwp->w_flag |= WFMODE|WFHARD;
  177. X                        mlwrite("[Old buffer]");
  178. X                        return (TRUE);
  179. X                }
  180. X        }
  181. X        makename(bname, fname);                 /* New buffer name.     */
  182. X        while ((bp=bfind(bname, FALSE, 0)) != NULL) {
  183. X                s = mlreply("Buffer name: ", bname, NBUFN);
  184. X                if (s == ABORT)                 /* ^G to just quit      */
  185. X                        return (s);
  186. X                if (s == FALSE) {               /* CR to clobber it     */
  187. X                        makename(bname, fname);
  188. X                        break;
  189. X                }
  190. X        }
  191. X        if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
  192. X                mlwrite("Cannot create buffer");
  193. X                return (FALSE);
  194. X        }
  195. X        if (--curbp->b_nwnd == 0) {             /* Undisplay.           */
  196. X                curbp->b_dotp = curwp->w_dotp;
  197. X                curbp->b_doto = curwp->w_doto;
  198. X                curbp->b_markp = curwp->w_markp;
  199. X                curbp->b_marko = curwp->w_marko;
  200. X        }
  201. X        curbp = bp;                             /* Switch to it.        */
  202. X        curwp->w_bufp = bp;
  203. X        curbp->b_nwnd++;
  204. X        return(readin(fname, lockfl));          /* Read it in.          */
  205. X}
  206. X
  207. X/*
  208. X * Read file "fname" into the current
  209. X * buffer, blowing away any text found there. Called
  210. X * by both the read and find commands. Return the final
  211. X * status of the read. Also called by the mainline,
  212. X * to read in a file specified on the command line as
  213. X * an argument. If the filename ends in a ".c", CMODE is
  214. X * set for the current buffer.
  215. X */
  216. Xreadin(fname, lockfl)
  217. X
  218. Xchar    fname[];    /* name of file to read */
  219. Xint    lockfl;        /* check for file locks? */
  220. X
  221. X{
  222. X        register LINE   *lp1;
  223. X        register LINE   *lp2;
  224. X        register int    i;
  225. X        register WINDOW *wp;
  226. X        register BUFFER *bp;
  227. X        register int    s;
  228. X        register int    nbytes;
  229. X        register int    nline;
  230. X    register char    *sptr;        /* pointer into filename string */
  231. X    int        lflag;        /* any lines longer than allowed? */
  232. X        char            line[NLINE];
  233. X
  234. X#if    FILOCK
  235. X    if (lockfl && lockchk(fname) == ABORT)
  236. X        return(ABORT);
  237. X#endif
  238. X#if    CRYPT
  239. X    s = resetkey();
  240. X    if (s != TRUE)
  241. X        return(s);
  242. X#endif
  243. X        bp = curbp;                             /* Cheap.               */
  244. X        if ((s=bclear(bp)) != TRUE)             /* Might be old.        */
  245. X                return (s);
  246. X        bp->b_flag &= ~(BFINVS|BFCHG);
  247. X#if    ACMODE
  248. X    if (strlen(fname) > 1) {        /* check if a 'C' file    */
  249. X        sptr = fname + strlen(fname) - 2;
  250. X        if (*sptr == '.' &&
  251. X           (*(sptr + 1) == 'c' || *(sptr + 1) == 'h'))
  252. X            bp->b_mode |= MDCMOD;
  253. X    }
  254. X#endif
  255. X        strcpy(bp->b_fname, fname);
  256. X
  257. X    /* turn off ALL keyboard translation in case we get a dos error */
  258. X    TTkclose();
  259. X
  260. X        if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
  261. X                goto out;
  262. X        if (s == FIOFNF) {                      /* File not found.      */
  263. X                mlwrite("[New file]");
  264. X                goto out;
  265. X        }
  266. X        mlwrite("[Reading file]");
  267. X        nline = 0;
  268. X    lflag = FALSE;
  269. X        while ((s=ffgetline(line, NLINE)) == FIOSUC || s == FIOLNG
  270. X            || s == FIOFUN) {
  271. X        if (s == FIOLNG) {
  272. X            lflag = TRUE;
  273. X            --nline;
  274. X        }
  275. X                nbytes = strlen(line);
  276. X                if ((lp1=lalloc(nbytes)) == NULL) {
  277. X                        s = FIOERR;             /* Keep message on the  */
  278. X                        break;                  /* display.             */
  279. X                }
  280. X                lp2 = lback(curbp->b_linep);
  281. X                lp2->l_fp = lp1;
  282. X                lp1->l_fp = curbp->b_linep;
  283. X                lp1->l_bp = lp2;
  284. X                curbp->b_linep->l_bp = lp1;
  285. X                for (i=0; i<nbytes; ++i)
  286. X                        lputc(lp1, i, line[i]);
  287. X                ++nline;
  288. X                if (s == FIOFUN)
  289. X                    break;
  290. X        }
  291. X        ffclose();                              /* Ignore errors.       */
  292. X    strcpy(line, "[");
  293. X    if (lflag)
  294. X        strcat(line, "Long lines wrapped, ");
  295. X    if (s == FIOFUN)
  296. X        strcat(line, "Funny line at EOF, ");
  297. X        if (s == FIOEOF || s == FIOFUN) {        /* Don't zap message!   */
  298. X        sprintf(&line[strlen(line)], "Read %d line", nline);
  299. X                if (nline > 1)
  300. X            strcat(line, "s");
  301. X        strcat(line, "]");
  302. X        }
  303. X    if (s != FIOERR)
  304. X        mlwrite(line);
  305. X
  306. Xout:
  307. X    TTkopen();    /* open the keyboard again */
  308. X        for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
  309. X                if (wp->w_bufp == curbp) {
  310. X                        wp->w_linep = lforw(curbp->b_linep);
  311. X                        wp->w_dotp  = lforw(curbp->b_linep);
  312. X                        wp->w_doto  = 0;
  313. X                        wp->w_markp = NULL;
  314. X                        wp->w_marko = 0;
  315. X                        wp->w_flag |= WFMODE|WFHARD;
  316. X                }
  317. X        }
  318. X        if (s == FIOERR || s == FIOFNF)        /* False if error.      */
  319. X                return(FALSE);
  320. X        return (TRUE);
  321. X}
  322. X
  323. X/*
  324. X * Take a file name, and from it
  325. X * fabricate a buffer name. This routine knows
  326. X * about the syntax of file names on the target system.
  327. X * I suppose that this information could be put in
  328. X * a better place than a line of code.
  329. X */
  330. Xmakename(bname, fname)
  331. Xchar    bname[];
  332. Xchar    fname[];
  333. X{
  334. X        register char   *cp1;
  335. X        register char   *cp2;
  336. X
  337. X        cp1 = &fname[0];
  338. X        while (*cp1 != 0)
  339. X                ++cp1;
  340. X
  341. X#if     AMIGA
  342. X        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='/')
  343. X                --cp1;
  344. X#endif
  345. X#if     VMS
  346. X        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!=']')
  347. X                --cp1;
  348. X#endif
  349. X#if     CPM
  350. X        while (cp1!=&fname[0] && cp1[-1]!=':')
  351. X                --cp1;
  352. X#endif
  353. X#if     MSDOS
  354. X        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\'&&cp1[-1]!='/')
  355. X                --cp1;
  356. X#endif
  357. X#if     FINDER
  358. X        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\'&&cp1[-1]!='/')
  359. X                --cp1;
  360. X#endif
  361. X#if     V7 | USG | BSD
  362. X        while (cp1!=&fname[0] && cp1[-1]!='/')
  363. X                --cp1;
  364. X#endif
  365. X        cp2 = &bname[0];
  366. X        while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  367. X                *cp2++ = *cp1++;
  368. X        *cp2 = 0;
  369. X}
  370. X
  371. X/*
  372. X * Ask for a file name, and write the
  373. X * contents of the current buffer to that file.
  374. X * Update the remembered file name and clear the
  375. X * buffer changed flag. This handling of file names
  376. X * is different from the earlier versions, and
  377. X * is more compatable with Gosling EMACS than
  378. X * with ITS EMACS. Bound to "C-X C-W".
  379. X */
  380. Xfilewrite(f, n)
  381. X{
  382. X        register WINDOW *wp;
  383. X        register int    s;
  384. X        char            fname[NFILEN];
  385. X
  386. X    if (restflag)        /* don't allow this command if restricted */
  387. X        return(resterr());
  388. X        if ((s=mlreply("Write file: ", fname, NFILEN)) != TRUE)
  389. X                return (s);
  390. X        if ((s=writeout(fname)) == TRUE) {
  391. X                strcpy(curbp->b_fname, fname);
  392. X                curbp->b_flag &= ~BFCHG;
  393. X                wp = wheadp;                    /* Update mode lines.   */
  394. X                while (wp != NULL) {
  395. X                        if (wp->w_bufp == curbp)
  396. X                                wp->w_flag |= WFMODE;
  397. X                        wp = wp->w_wndp;
  398. X                }
  399. X        }
  400. X        return (s);
  401. X}
  402. X
  403. X/*
  404. X * Save the contents of the current
  405. X * buffer in its associatd file. No nothing
  406. X * if nothing has changed (this may be a bug, not a
  407. X * feature). Error if there is no remembered file
  408. X * name for the buffer. Bound to "C-X C-S". May
  409. X * get called by "C-Z".
  410. X */
  411. Xfilesave(f, n)
  412. X{
  413. X        register WINDOW *wp;
  414. X        register int    s;
  415. X
  416. X    if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  417. X        return(rdonly());    /* we are in read only mode    */
  418. X        if ((curbp->b_flag&BFCHG) == 0)         /* Return, no changes.  */
  419. X                return (TRUE);
  420. X        if (curbp->b_fname[0] == 0) {           /* Must have a name.    */
  421. X                mlwrite("No file name");
  422. X                return (FALSE);
  423. X        }
  424. X        if ((s=writeout(curbp->b_fname)) == TRUE) {
  425. X                curbp->b_flag &= ~BFCHG;
  426. X                wp = wheadp;                    /* Update mode lines.   */
  427. X                while (wp != NULL) {
  428. X                        if (wp->w_bufp == curbp)
  429. X                                wp->w_flag |= WFMODE;
  430. X                        wp = wp->w_wndp;
  431. X                }
  432. X        }
  433. X        return (s);
  434. X}
  435. X
  436. X/*
  437. X * This function performs the details of file
  438. X * writing. Uses the file management routines in the
  439. X * "fileio.c" package. The number of lines written is
  440. X * displayed. Sadly, it looks inside a LINE; provide
  441. X * a macro for this. Most of the grief is error
  442. X * checking of some sort.
  443. X */
  444. Xwriteout(fn)
  445. Xchar    *fn;
  446. X{
  447. X        register int    s;
  448. X        register LINE   *lp;
  449. X        register int    nline;
  450. X
  451. X#if    CRYPT
  452. X    s = resetkey();
  453. X    if (s != TRUE)
  454. X        return(s);
  455. X#endif
  456. X    /* turn off ALL keyboard translation in case we get a dos error */
  457. X    TTkclose();
  458. X
  459. X        if ((s=ffwopen(fn)) != FIOSUC) {        /* Open writes message. */
  460. X        TTkopen();
  461. X                return (FALSE);
  462. X        }
  463. X    mlwrite("[Writing..]");            /* tell us were writing */
  464. X        lp = lforw(curbp->b_linep);             /* First line.          */
  465. X        nline = 0;                              /* Number of lines.     */
  466. X        while (lp != curbp->b_linep) {
  467. X                if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
  468. X                        break;
  469. X                ++nline;
  470. X                lp = lforw(lp);
  471. X        }
  472. X        if (s == FIOSUC) {                      /* No write error.      */
  473. X                s = ffclose();
  474. X                if (s == FIOSUC) {              /* No close error.      */
  475. X                        if (nline == 1)
  476. X                                mlwrite("[Wrote 1 line]");
  477. X                        else
  478. X                                mlwrite("[Wrote %d lines]", nline);
  479. X                }
  480. X        } else                                  /* Ignore close error   */
  481. X                ffclose();                      /* if a write error.    */
  482. X    TTkopen();
  483. X        if (s != FIOSUC)                        /* Some sort of error.  */
  484. X                return (FALSE);
  485. X        return (TRUE);
  486. X}
  487. X
  488. X/*
  489. X * The command allows the user
  490. X * to modify the file name associated with
  491. X * the current buffer. It is like the "f" command
  492. X * in UNIX "ed". The operation is simple; just zap
  493. X * the name in the BUFFER structure, and mark the windows
  494. X * as needing an update. You can type a blank line at the
  495. X * prompt if you wish.
  496. X */
  497. Xfilename(f, n)
  498. X{
  499. X        register WINDOW *wp;
  500. X        register int    s;
  501. X        char            fname[NFILEN];
  502. X
  503. X    if (restflag)        /* don't allow this command if restricted */
  504. X        return(resterr());
  505. X        if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
  506. X                return (s);
  507. X        if (s == FALSE)
  508. X                strcpy(curbp->b_fname, "");
  509. X        else
  510. X                strcpy(curbp->b_fname, fname);
  511. X        wp = wheadp;                            /* Update mode lines.   */
  512. X        while (wp != NULL) {
  513. X                if (wp->w_bufp == curbp)
  514. X                        wp->w_flag |= WFMODE;
  515. X                wp = wp->w_wndp;
  516. X        }
  517. X    curbp->b_mode &= ~MDVIEW;    /* no longer read only mode */
  518. X        return (TRUE);
  519. X}
  520. X
  521. X/*
  522. X * Insert file "fname" into the current
  523. X * buffer, Called by insert file command. Return the final
  524. X * status of the read.
  525. X */
  526. Xifile(fname)
  527. Xchar    fname[];
  528. X{
  529. X        register LINE   *lp0;
  530. X        register LINE   *lp1;
  531. X        register LINE   *lp2;
  532. X        register int    i;
  533. X        register BUFFER *bp;
  534. X        register int    s;
  535. X        register int    nbytes;
  536. X        register int    nline;
  537. X    int        lflag;        /* any lines longer than allowed? */
  538. X        char            line[NLINE];
  539. X
  540. X        bp = curbp;                             /* Cheap.               */
  541. X        bp->b_flag |= BFCHG;            /* we have changed    */
  542. X    bp->b_flag &= ~BFINVS;            /* and are not temporary*/
  543. X        if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
  544. X                goto out;
  545. X        if (s == FIOFNF) {                      /* File not found.      */
  546. X                mlwrite("[No such file]");
  547. X        return(FALSE);
  548. X        }
  549. X        mlwrite("[Inserting file]");
  550. X
  551. X#if    CRYPT
  552. X    s = resetkey();
  553. X    if (s != TRUE)
  554. X        return(s);
  555. X#endif
  556. X    /* back up a line and save the mark here */
  557. X    curwp->w_dotp = lback(curwp->w_dotp);
  558. X    curwp->w_doto = 0;
  559. X    curwp->w_markp = curwp->w_dotp;
  560. X    curwp->w_marko = 0;
  561. X
  562. X        nline = 0;
  563. X    lflag = FALSE;
  564. X        while ((s=ffgetline(line, NLINE)) == FIOSUC || s == FIOLNG
  565. X            || s == FIOFUN) {
  566. X        if (s == FIOLNG) {
  567. X            lflag = TRUE;
  568. X            --nline;
  569. X        }
  570. X                nbytes = strlen(line);
  571. X                if ((lp1=lalloc(nbytes)) == NULL) {
  572. X                        s = FIOERR;             /* Keep message on the  */
  573. X                        break;                  /* display.             */
  574. X                }
  575. X        lp0 = curwp->w_dotp;    /* line previous to insert */
  576. X        lp2 = lp0->l_fp;    /* line after insert */
  577. X
  578. X        /* re-link new line between lp0 and lp2 */
  579. X        lp2->l_bp = lp1;
  580. X        lp0->l_fp = lp1;
  581. X        lp1->l_bp = lp0;
  582. X        lp1->l_fp = lp2;
  583. X
  584. X        /* and advance and write out the current line */
  585. X        curwp->w_dotp = lp1;
  586. X                for (i=0; i<nbytes; ++i)
  587. X                        lputc(lp1, i, line[i]);
  588. X                ++nline;
  589. X                if (s == FIOFUN)
  590. X                    break;
  591. X        }
  592. X        ffclose();                              /* Ignore errors.       */
  593. X    curwp->w_markp = lforw(curwp->w_markp);
  594. X    strcpy(line, "[");
  595. X    if (lflag)
  596. X        strcat(line, "Long lines wrapped, ");
  597. X    if (s == FIOFUN)
  598. X        strcat(line, "Funny line at EOF, ");
  599. X        if (s == FIOEOF || s == FIOFUN) {        /* Don't zap message!   */
  600. X        sprintf(&line[strlen(line)], "Inserted %d line", nline);
  601. X                if (nline > 1)
  602. X            strcat(line, "s");
  603. X        strcat(line, "]");
  604. X        }
  605. X    if (s != FIOERR)
  606. X        mlwrite(line);
  607. Xout:
  608. X    /* advance to the next line and mark the window for changes */
  609. X    curwp->w_dotp = lforw(curwp->w_dotp);
  610. X    curwp->w_flag |= WFHARD | WFMODE;
  611. X
  612. X    /* copy window parameters back to the buffer structure */
  613. X    curbp->b_dotp = curwp->w_dotp;
  614. X    curbp->b_doto = curwp->w_doto;
  615. X    curbp->b_markp = curwp->w_markp;
  616. X    curbp->b_marko = curwp->w_marko;
  617. X
  618. X        if (s == FIOERR)                        /* False if error.      */
  619. X                return (FALSE);
  620. X        return (TRUE);
  621. X}
  622. END_OF_file.c
  623. if test 18229 -ne `wc -c <file.c`; then
  624.     echo shar: \"file.c\" unpacked with wrong size!
  625. fi
  626. # end of overwriting check
  627. fi
  628. echo shar: Extracting \"isearch.c\" \(18764 characters\)
  629. if test -f isearch.c ; then 
  630.   echo shar: Will not over-write existing file \"isearch.c\"
  631. else
  632. sed "s/^X//" >isearch.c <<'END_OF_isearch.c'
  633. X/*
  634. X * The functions in this file implement commands that perform incremental
  635. X * searches in the forward and backward directions.  This "ISearch" command
  636. X * is intended to emulate the same command from the original EMACS 
  637. X * implementation (ITS).  Contains references to routines internal to
  638. X * SEARCH.C.
  639. X *
  640. X * REVISION HISTORY:
  641. X *
  642. X *    D. R. Banks 9-May-86
  643. X *    - added ITS EMACSlike ISearch
  644. X *
  645. X *    John M. Gamble 5-Oct-86
  646. X *    - Made iterative search use search.c's scanner() routine.
  647. X *      This allowed the elimination of bakscan().
  648. X *    - Put isearch constants into esearch.h
  649. X *    - Eliminated the passing of 'status' to scanmore() and
  650. X *      checknext(), since there were no circumstances where
  651. X *      it ever equalled FALSE.
  652. X */
  653. X
  654. X#include        <stdio.h>
  655. X#include    "estruct.h"
  656. X#include        "edef.h"
  657. X#include    "esearch.h"
  658. X
  659. X#if    ISRCH
  660. X
  661. Xextern int scanner();            /* Handy search routine */
  662. Xextern int eq();            /* Compare chars, match case */
  663. Xextern char tap[];            /* Reverse pattern array.*/
  664. X/* A couple of "own" variables for re-eat */
  665. X
  666. Xint    (*saved_get_char)();        /* Get character routine */
  667. Xint    eaten_char = -1;        /* Re-eaten char */
  668. X
  669. X/* A couple more "own" variables for the command string */
  670. X
  671. Xint    cmd_buff[CMDBUFLEN];        /* Save the command args here */
  672. Xint    cmd_offset;            /* Current offset into command buff */
  673. Xint    cmd_reexecute = -1;        /* > 0 if re-executing command */
  674. X
  675. X
  676. X/*
  677. X * Subroutine to do incremental reverse search.  It actually uses the
  678. X * same code as the normal incremental search, as both can go both ways.
  679. X */
  680. Xint risearch(f, n)
  681. X{
  682. X    LINE *curline;            /* Current line on entry          */
  683. X    int  curoff;            /* Current offset on entry          */
  684. X
  685. X    /* remember the initial . on entry: */
  686. X
  687. X    curline = curwp->w_dotp;        /* Save the current line pointer      */
  688. X    curoff  = curwp->w_doto;        /* Save the current offset          */
  689. X
  690. X    /* Make sure the search doesn't match where we already are:              */
  691. X
  692. X    backchar(TRUE, 1);            /* Back up a character              */
  693. X
  694. X    if (!(isearch(f, -n)))        /* Call ISearch backwards          */
  695. X    {                    /* If error in search:              */
  696. X    curwp->w_dotp = curline;    /* Reset the line pointer          */
  697. X    curwp->w_doto = curoff;        /*  and the offset to original value  */
  698. X    curwp->w_flag |= WFMOVE;    /* Say we've moved              */
  699. X    update(FALSE);            /* And force an update              */
  700. X    mlwrite ("[search failed]");    /* Say we died                  */
  701. X    } else mlerase ();            /* If happy, just erase the cmd line  */
  702. X}
  703. X
  704. X/* Again, but for the forward direction */
  705. X
  706. Xint fisearch(f, n)
  707. X{
  708. X    LINE *curline;            /* Current line on entry          */
  709. X    int  curoff;            /* Current offset on entry          */
  710. X
  711. X    /* remember the initial . on entry: */
  712. X
  713. X    curline = curwp->w_dotp;        /* Save the current line pointer      */
  714. X    curoff  = curwp->w_doto;        /* Save the current offset          */
  715. X
  716. X    /* do the search */
  717. X
  718. X    if (!(isearch(f, n)))        /* Call ISearch forwards          */
  719. X    {                    /* If error in search:              */
  720. X    curwp->w_dotp = curline;    /* Reset the line pointer          */
  721. X    curwp->w_doto = curoff;        /*  and the offset to original value  */
  722. X    curwp->w_flag |= WFMOVE;    /* Say we've moved              */
  723. X    update(FALSE);            /* And force an update              */
  724. X    mlwrite ("[search failed]");    /* Say we died                  */
  725. X    } else mlerase ();            /* If happy, just erase the cmd line  */
  726. X}
  727. X
  728. X/*
  729. X * Subroutine to do an incremental search.  In general, this works similarly
  730. X * to the older micro-emacs search function, except that the search happens
  731. X * as each character is typed, with the screen and cursor updated with each
  732. X * new search character.
  733. X *
  734. X * While searching forward, each successive character will leave the cursor
  735. X * at the end of the entire matched string.  Typing a Control-S or Control-X
  736. X * will cause the next occurrence of the string to be searched for (where the
  737. X * next occurrence does NOT overlap the current occurrence).  A Control-R will
  738. X * change to a backwards search, META will terminate the search and Control-G
  739. X * will abort the search.  Rubout will back up to the previous match of the
  740. X * string, or if the starting point is reached first, it will delete the
  741. X * last character from the search string.
  742. X *
  743. X * While searching backward, each successive character will leave the cursor
  744. X * at the beginning of the matched string.  Typing a Control-R will search
  745. X * backward for the next occurrence of the string.  Control-S or Control-X
  746. X * will revert the search to the forward direction.  In general, the reverse
  747. X * incremental search is just like the forward incremental search inverted.
  748. X *
  749. X * In all cases, if the search fails, the user will be feeped, and the search
  750. X * will stall until the pattern string is edited back into something that
  751. X * exists (or until the search is aborted).
  752. X */
  753. Xisearch(f, n)
  754. X{
  755. X    int            status;        /* Search status */
  756. X    int            col;        /* prompt column */
  757. X    register int    cpos;        /* character number in search string  */
  758. X    register int    c;        /* current input character */
  759. X    register int    expc;        /* function expanded input char          */
  760. X    char        pat_save[NPAT];    /* Saved copy of the old pattern str  */
  761. X    LINE        *curline;    /* Current line on entry          */
  762. X    int            curoff;        /* Current offset on entry          */
  763. X    int            init_direction;    /* The initial search direction          */
  764. X
  765. X    /* Initialize starting conditions */
  766. X
  767. X    cmd_reexecute = -1;        /* We're not re-executing (yet?)      */
  768. X    cmd_offset = 0;            /* Start at the beginning of the buff */
  769. X    cmd_buff[0] = '\0';        /* Init the command buffer          */
  770. X    strncpy (pat_save, pat, NPAT);    /* Save the old pattern string          */
  771. X    curline = curwp->w_dotp;        /* Save the current line pointer      */
  772. X    curoff  = curwp->w_doto;        /* Save the current offset          */
  773. X    init_direction = n;            /* Save the initial search direction  */
  774. X
  775. X    /* This is a good place to start a re-execution: */
  776. X
  777. Xstart_over:
  778. X
  779. X    /* ask the user for the text of a pattern */
  780. X    col = promptpattern("ISearch: ");        /* Prompt, remember the col   */
  781. X
  782. X    cpos = 0;                    /* Start afresh              */
  783. X    status = TRUE;                /* Assume everything's cool   */
  784. X
  785. X    /*
  786. X       Get the first character in the pattern.  If we get an initial Control-S
  787. X       or Control-R, re-use the old search string and find the first occurrence
  788. X     */
  789. X
  790. X    c = ectoc(expc = get_char());        /* Get the first character    */
  791. X    if ((c == IS_FORWARD) ||
  792. X        (c == IS_REVERSE) ||
  793. X        (c == IS_VMSFORW))            /* Reuse old search string?   */
  794. X    {
  795. X        for (cpos = 0; pat[cpos] != 0; cpos++)    /* Yup, find the length          */
  796. X            col = echochar(pat[cpos],col);    /*  and re-echo the string    */
  797. X    if (c == IS_REVERSE) {            /* forward search?          */
  798. X        n = -1;                /* No, search in reverse      */
  799. X        backchar (TRUE, 1);            /* Be defensive about EOB     */
  800. X    } else
  801. X        n = 1;                /* Yes, search forward          */
  802. X    status = scanmore(pat, n);        /* Do the search          */
  803. X    c = ectoc(expc = get_char());        /* Get another character      */
  804. X    }
  805. X
  806. X    /* Top of the per character loop */
  807. X            
  808. X    for (;;)                    /* ISearch per character loop */
  809. X    {
  810. X    /* Check for special characters first: */
  811. X    /* Most cases here change the search */
  812. X
  813. X    if (expc == metac)            /* Want to quit searching?    */
  814. X        return (TRUE);            /* Quit searching now          */
  815. X
  816. X    switch (c)                /* dispatch on the input char */
  817. X    {
  818. X      case IS_ABORT:            /* If abort search request    */
  819. X        return(FALSE);            /* Quit searching again          */
  820. X
  821. X      case IS_REVERSE:            /* If backward search          */
  822. X      case IS_FORWARD:            /* If forward search          */
  823. X      case IS_VMSFORW:            /*  of either flavor          */
  824. X        if (c == IS_REVERSE)        /* If reverse search          */
  825. X        n = -1;                /* Set the reverse direction  */
  826. X        else                /* Otherwise,               */
  827. X        n = 1;                /*  go forward              */
  828. X        status = scanmore(pat, n);        /* Start the search again     */
  829. X        c = ectoc(expc = get_char());    /* Get the next char          */
  830. X        continue;                /* Go continue with the search*/
  831. X
  832. X      case IS_NEWLINE:            /* Carriage return          */
  833. X        c = '\n';                /* Make it a new line          */
  834. X        break;                /* Make sure we use it          */
  835. X
  836. X      case IS_QUOTE:            /* Quote character          */
  837. X      case IS_VMSQUOTE:            /*  of either variety          */
  838. X        c = ectoc(expc = get_char());    /* Get the next char          */
  839. X
  840. X      case IS_TAB:                /* Generically allowed          */
  841. X      case '\n':                /*  controlled characters     */
  842. X        break;                /* Make sure we use it          */
  843. X
  844. X      case IS_BACKSP:            /* If a backspace:            */
  845. X      case IS_RUBOUT:            /*  or if a Rubout:          */
  846. X        if (cmd_offset <= 1)        /* Anything to delete?          */
  847. X        return (TRUE);            /* No, just exit          */
  848. X        --cmd_offset;            /* Back up over the Rubout    */
  849. X        cmd_buff[--cmd_offset] = '\0';    /* Yes, delete last char   */
  850. X        curwp->w_dotp = curline;        /* Reset the line pointer     */
  851. X        curwp->w_doto = curoff;        /*  and the offset          */
  852. X        n = init_direction;            /* Reset the search direction */
  853. X        strncpy (pat, pat_save, NPAT);    /* Restore the old search str */
  854. X        cmd_reexecute = 0;            /* Start the whole mess over  */
  855. X        goto start_over;            /* Let it take care of itself */
  856. X
  857. X      /* Presumably a quasi-normal character comes here */
  858. X
  859. X      default:                /* All other chars              */
  860. X        if (c < ' ')            /* Is it printable?          */
  861. X        {                    /* Nope.              */
  862. X        reeat (c);            /* Re-eat the char          */
  863. X        return (TRUE);            /* And return the last status */
  864. X        }
  865. X    }  /* Switch */
  866. X
  867. X    /* I guess we got something to search for, so search for it          */
  868. X
  869. X    pat[cpos++] = c;            /* put the char in the buffer */
  870. X    if (cpos >= NPAT)            /* too many chars in string?  */
  871. X    {                    /* Yup.  Complain about it    */
  872. X        mlwrite("? Search string too long");
  873. X        return(TRUE);            /* Return an error          */
  874. X    }
  875. X    pat[cpos] = 0;                /* null terminate the buffer  */
  876. X    col = echochar(c,col);            /* Echo the character          */
  877. X    if (!status) {                /* If we lost last time          */
  878. X        TTputc(BELL);        /* Feep again              */
  879. X        TTflush();            /* see that the feep feeps    */
  880. X    } else                    /* Otherwise, we must have won*/
  881. X        if (!(status = checknext(c, pat, n))) /* See if match          */
  882. X        status = scanmore(pat, n);    /*  or find the next match    */
  883. X    c = ectoc(expc = get_char());        /* Get the next char          */
  884. X    } /* for {;;} */
  885. X}
  886. X
  887. X/*
  888. X * Trivial routine to insure that the next character in the search string is
  889. X * still true to whatever we're pointing to in the buffer.  This routine will
  890. X * not attempt to move the "point" if the match fails, although it will 
  891. X * implicitly move the "point" if we're forward searching, and find a match,
  892. X * since that's the way forward isearch works.
  893. X *
  894. X * If the compare fails, we return FALSE and assume the caller will call
  895. X * scanmore or something.
  896. X */
  897. X
  898. Xint checknext (chr, patrn, dir)    /* Check next character in search string */
  899. Xchar    chr;            /* Next char to look for         */
  900. Xchar    *patrn;            /* The entire search string (incl chr)   */
  901. Xint    dir;            /* Search direction             */
  902. X{
  903. X    register LINE *curline;        /* current line during scan          */
  904. X    register int curoff;        /* position within current line          */
  905. X    register int buffchar;        /* character at current position      */
  906. X    int status;                /* how well things go              */
  907. X
  908. X
  909. X    /* setup the local scan pointer to current "." */
  910. X
  911. X    curline = curwp->w_dotp;        /* Get the current line structure     */
  912. X    curoff  = curwp->w_doto;        /* Get the offset within that line    */
  913. X
  914. X    if (dir > 0)            /* If searching forward              */
  915. X    {
  916. X        if (curoff == llength(curline)) /* If at end of line              */
  917. X        {
  918. X        curline = lforw(curline);    /* Skip to the next line          */
  919. X        if (curline == curbp->b_linep)
  920. X        return (FALSE);        /* Abort if at end of buffer          */
  921. X        curoff = 0;            /* Start at the beginning of the line */
  922. X        buffchar = '\n';        /* And say the next char is NL          */
  923. X    } else
  924. X        buffchar = lgetc(curline, curoff++); /* Get the next char          */
  925. X    if (status = eq(buffchar, chr))    /* Is it what we're looking for?      */
  926. X    {
  927. X        curwp->w_dotp = curline;    /* Yes, set the buffer's point          */
  928. X        curwp->w_doto = curoff;    /*  to the matched character          */
  929. X        curwp->w_flag |= WFMOVE;    /* Say that we've moved              */
  930. X    }
  931. X    return (status);        /* And return the status          */
  932. X    } else                /* Else, if reverse search:          */
  933. X    return (match_pat (patrn));    /* See if we're in the right place    */
  934. X}
  935. X
  936. X/*
  937. X * This hack will search for the next occurrence of <pat> in the buffer, either
  938. X * forward or backward.  It is called with the status of the prior search
  939. X * attempt, so that it knows not to bother if it didn't work last time.  If
  940. X * we can't find any more matches, "point" is left where it was before.  If
  941. X * we do find a match, "point" will be at the end of the matched string for
  942. X * forward searches and at the beginning of the matched string for reverse
  943. X * searches.
  944. X */
  945. Xint scanmore(patrn, dir)    /* search forward or back for a pattern          */
  946. Xchar    *patrn;            /* string to scan for                  */
  947. Xint    dir;            /* direction to search                  */
  948. X{
  949. X    int    sts;            /* search status              */
  950. X
  951. X        if (dir < 0)                /* reverse search?          */
  952. X        {
  953. X        rvstrcpy(tap, patrn);        /* Put reversed string in tap */
  954. X        sts = scanner(tap, REVERSE, PTBEG);
  955. X    }
  956. X    else
  957. X        sts = scanner(patrn, FORWARD, PTEND);    /* Nope. Go forward   */
  958. X
  959. X    if (!sts)
  960. X    {
  961. X        TTputc(BELL);    /* Feep if search fails       */
  962. X        TTflush();        /* see that the feep feeps    */
  963. X    }
  964. X
  965. X    return(sts);                /* else, don't even try          */
  966. X}
  967. X
  968. X/*
  969. X * The following is a worker subroutine used by the reverse search.  It
  970. X * compares the pattern string with the characters at "." for equality. If
  971. X * any characters mismatch, it will return FALSE.
  972. X *
  973. X * This isn't used for forward searches, because forward searches leave "."
  974. X * at the end of the search string (instead of in front), so all that needs to
  975. X * be done is match the last char input.
  976. X */
  977. X
  978. Xint match_pat (patrn)    /* See if the pattern string matches string at "."   */
  979. Xchar    *patrn;        /* String to match to buffer                 */
  980. X{
  981. X    register int  i;            /* Generic loop index/offset          */
  982. X    register int buffchar;        /* character at current position      */
  983. X    register LINE *curline;        /* current line during scan          */
  984. X    register int curoff;        /* position within current line          */
  985. X
  986. X    /* setup the local scan pointer to current "." */
  987. X
  988. X    curline = curwp->w_dotp;        /* Get the current line structure     */
  989. X    curoff  = curwp->w_doto;        /* Get the offset within that line    */
  990. X
  991. X    /* top of per character compare loop: */
  992. X
  993. X    for (i = 0; i < strlen(patrn); i++)    /* Loop for all characters in patrn   */
  994. X    {
  995. X        if (curoff == llength(curline)) /* If at end of line              */
  996. X        {
  997. X        curline = lforw(curline);    /* Skip to the next line          */
  998. X        curoff = 0;            /* Start at the beginning of the line */
  999. X        if (curline == curbp->b_linep)
  1000. X        return (FALSE);        /* Abort if at end of buffer          */
  1001. X        buffchar = '\n';        /* And say the next char is NL          */
  1002. X    } else
  1003. X        buffchar = lgetc(curline, curoff++); /* Get the next char          */
  1004. X    if (!eq(buffchar, patrn[i]))    /* Is it what we're looking for?      */
  1005. X        return (FALSE);        /* Nope, just punt it then          */
  1006. X    }
  1007. X    return (TRUE);            /* Everything matched? Let's celebrate*/
  1008. X}
  1009. X
  1010. X/* Routine to prompt for I-Search string. */
  1011. X
  1012. Xint promptpattern(prompt)
  1013. Xchar *prompt;
  1014. X{
  1015. X    char tpat[NPAT+20];
  1016. X
  1017. X    strcpy(tpat, prompt);        /* copy prompt to output string */
  1018. X    strcat(tpat, " [");            /* build new prompt string */
  1019. X    expandp(pat, &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  1020. X    strcat(tpat, "]<META>: ");
  1021. X
  1022. X    /* check to see if we are executing a command line */
  1023. X    if (!clexec) {
  1024. X    mlwrite(tpat);
  1025. X    }
  1026. X    return(strlen(tpat));
  1027. X}
  1028. X
  1029. X/* routine to echo i-search characters */
  1030. X
  1031. Xint echochar(c,col)
  1032. Xint    c;    /* character to be echoed */
  1033. Xint    col;    /* column to be echoed in */
  1034. X{
  1035. X    movecursor(term.t_nrow,col);        /* Position the cursor          */
  1036. X    if ((c < ' ') || (c == 0x7F))        /* Control character?          */
  1037. X    {
  1038. X    switch (c)                /* Yes, dispatch special cases*/
  1039. X    {
  1040. X      case '\n':                /* Newline              */
  1041. X        TTputc('<');
  1042. X        TTputc('N');
  1043. X        TTputc('L');
  1044. X        TTputc('>');
  1045. X        col += 3;
  1046. X        break;
  1047. X
  1048. X      case '\t':                /* Tab                  */
  1049. X        TTputc('<');
  1050. X        TTputc('T');
  1051. X        TTputc('A');
  1052. X        TTputc('B');
  1053. X        TTputc('>');
  1054. X        col += 4;
  1055. X        break;
  1056. X
  1057. X      case 0x7F:                /* Rubout:              */
  1058. X        TTputc('^');        /* Output a funny looking     */
  1059. X        TTputc('?');        /*  indication of Rubout      */
  1060. X        col++;                /* Count the extra char       */
  1061. X        break;
  1062. X
  1063. X      default:                /* Vanilla control char       */
  1064. X        TTputc('^');        /* Yes, output prefix          */
  1065. X            TTputc(c+0x40);        /* Make it "^X"              */
  1066. X        col++;                /* Count this char          */
  1067. X    }
  1068. X    } else
  1069. X    TTputc(c);            /* Otherwise, output raw char */
  1070. X    TTflush();                /* Flush the output          */
  1071. X    return(++col);                /* return the new column no   */
  1072. X}
  1073. X
  1074. X/*
  1075. X * Routine to get the next character from the input stream.  If we're reading
  1076. X * from the real terminal, force a screen update before we get the char. 
  1077. X * Otherwise, we must be re-executing the command string, so just return the
  1078. X * next character.
  1079. X */
  1080. X
  1081. Xint get_char ()
  1082. X{
  1083. X    int    c;                /* A place to get a character          */
  1084. X
  1085. X    /* See if we're re-executing: */
  1086. X
  1087. X    if (cmd_reexecute >= 0)        /* Is there an offset?              */
  1088. X    if ((c = cmd_buff[cmd_reexecute++]) != 0)
  1089. X        return (c);            /* Yes, return any character          */
  1090. X
  1091. X    /* We're not re-executing (or aren't any more).  Try for a real char      */
  1092. X
  1093. X    cmd_reexecute = -1;        /* Say we're in real mode again          */
  1094. X    update(FALSE);            /* Pretty up the screen              */
  1095. X    if (cmd_offset >= CMDBUFLEN-1)    /* If we're getting too big ...          */
  1096. X    {
  1097. X    mlwrite ("? command too long");    /* Complain loudly and bitterly          */
  1098. X    return (metac);            /* And force a quit              */
  1099. X    }
  1100. X    c = get1key();        /* Get the next character          */
  1101. X    cmd_buff[cmd_offset++] = c; /* Save the char for next time        */
  1102. X    cmd_buff[cmd_offset] = '\0';/* And terminate the buffer          */
  1103. X    return (c);                /* Return the character              */
  1104. X}
  1105. X
  1106. X/*
  1107. X * Hacky routine to re-eat a character.  This will save the character to be
  1108. X * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  1109. X */
  1110. X
  1111. X/* Come here on the next term.t_getchar call: */
  1112. X
  1113. Xint uneat()
  1114. X{
  1115. X    int c;
  1116. X
  1117. X    term.t_getchar = saved_get_char;    /* restore the routine address          */
  1118. X    c = eaten_char;            /* Get the re-eaten char          */
  1119. X    eaten_char = -1;            /* Clear the old char              */
  1120. X    return(c);                /* and return the last char          */
  1121. X}
  1122. X
  1123. Xint reeat(c)
  1124. Xint    c;
  1125. X{
  1126. X    if (eaten_char != -1)        /* If we've already been here          */
  1127. X    return (NULL);            /* Don't do it again              */
  1128. X    eaten_char = c;            /* Else, save the char for later      */
  1129. X    saved_get_char = term.t_getchar;    /* Save the char get routine          */
  1130. X    term.t_getchar = uneat;        /* Replace it with ours              */
  1131. X}
  1132. X#else
  1133. Xisearch()
  1134. X{
  1135. X}
  1136. X#endif
  1137. END_OF_isearch.c
  1138. if test 18764 -ne `wc -c <isearch.c`; then
  1139.     echo shar: \"isearch.c\" unpacked with wrong size!
  1140. fi
  1141. # end of overwriting check
  1142. fi
  1143. echo shar: End of archive 9 \(of 14\).
  1144. cp /dev/null ark9isdone
  1145. MISSING=""
  1146. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  1147.     if test ! -f ark${I}isdone ; then
  1148.     MISSING="${MISSING} ${I}"
  1149.     fi
  1150. done
  1151. if test "${MISSING}" = "" ; then
  1152.     echo You have unpacked all 14 archives.
  1153.     echo "See the readme file"
  1154.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1155. else
  1156.     echo You still need to unpack the following archives:
  1157.     echo "        " ${MISSING}
  1158. fi
  1159. ##  End of shell archive.
  1160. exit 0
  1161.